#ifndef COMMON_SOURCE_CPP_ERPC_LIST_CAST_HPP_
#define COMMON_SOURCE_CPP_ERPC_LIST_CAST_HPP_
#include <ostream>
#include <cstring>
#include <gf_utility.h>
#include <cast_utilits.h>
#include <assert_macros.h>
namespace net {	namespace gdface {	namespace utils {
	has_member(elements)
	has_member(elementsCount)
	has_member(data)
	has_member(dataLength)

	template<typename T,typename Enable>
	struct _is_list_struct
		: std::false_type {
		using value_type = Enable;
	};
	template<typename T>
	struct _is_list_struct<T, bool>
		: std::true_type {
		using value_type = decltype(T::elements);
	};
	template<typename T, typename Enable = typename std::conditional<has_member_elements<T>::value && has_member_elementsCount<T>::value, bool,void>::type>
	struct is_list_struct 
		: _is_list_struct<T,Enable> {
	};

	template<typename T>
	struct is_binary
		: std::integral_constant<bool, has_member_data<T>::value && has_member_dataLength<T>::value>{
	};

	template<typename T, typename Enable>
	struct _is_list_binary : std::false_type {
	};
	template<typename T>
	struct _is_list_binary<T,bool> 
		: std::integral_constant<bool, is_binary<typename std::remove_pointer<decltype(T::elements)>::type>::value> {
	};
	template<typename T,typename Enable = typename std::conditional<is_list_struct<T>::value, bool, void>::type>
	struct is_list_binary
		: _is_list_binary<T, Enable> {
	};
	
	template<typename T>
	struct is_list_string 
		:std::integral_constant<bool, std::is_same<typename is_list_struct<T>::value_type, char**>::value> {
	};

	template<typename T>
	struct is_common_list_struct 
		: std::integral_constant<bool, is_list_struct<T>::value && !is_list_string<T>::value>{
	};
	//template<typename T,typename Enable = void>
	//struct is_list_container 
	//	: std::false_type {
	//};
	template<typename T>
	struct is_list_container 
		: std::integral_constant<bool, !std::is_same<T, std::string>::value> {
	};
	/**
	* alloc memory for list struct,such as list_<type>_1_t
	*/
	template<typename T,
		typename value_type_p = decltype(T::elements),
		typename value_type = typename std::remove_pointer<value_type_p>::type>
	void alloc_list_struct(T & t, size_t size) {
		if (nullptr == t.elements) {
			t.elementsCount = (decltype(t.elementsCount))size;
			t.elements = (value_type_p)malloc(sizeof(value_type)*t.elementsCount);
			throw_except_if_false(std::bad_alloc, t.elements);
		}
		else if (t.elementsCount != size) {
			auto p = std::realloc(t.elements, sizeof(value_type)*size);
			if (p) {
				t.elements = (value_type_p)p;
				t.elementsCount = (decltype(t.elementsCount))size;
			}
		}
	}
	// thrift stub type to eRPC type(array)
	template<typename L, typename E>
	typename std::enable_if<!std::is_pointer<E>::value && is_list_container<L>::value, void>::type
	cast(const L& left, E* right, size_t count)
	{
		auto c = left.size();
		if (c > count) c = count;
		if (right && c > 0) {
			for (auto i = 0; i < c; ++i) {
				cast(left[i], right[i]);
			}
		}
	}
	// thrift stub type to eRPC list type(ref)
	template<typename L, typename R>
	typename std::enable_if<is_list_container<L>::value && is_common_list_struct<R>::value, void>::type
	cast(const L& left, R&right)
	{
		alloc_list_struct(right, left.size());
		cast(left, right.elements, right.elementsCount);
	}
	// thrift stub type to eRPC list type(pointer)
	template<typename L, typename R>
	typename std::enable_if<is_list_struct<R>::value && is_list_container<L>::value, R>::type
	cast(const L& left, R*right)
	{
		if (right) {
			cast(left, *right);
			return *right;
		}
		else {
			R r;
			cast(left, r);
			return r;
		}
	}
	//######## eRPC type to thrift stub type ##########
	// eRPC type(array) to thrift stub type
	template<typename E, typename R>
	typename std::enable_if<is_list_container<R>::value>::type
	cast(const E*left, size_t count, R& right)
	{
		if (left && count > 0) {
			typename R::value_type r;
			for (auto i = 0; i < count; ++i) {
				cast(left[i], r);
				right.emplace_back(std::move(r));
			}
		}
	}
	// eRPC list type(ref) to thrift stub type(ref)
	template<typename L, typename R>
	typename std::enable_if<is_list_struct<L>::value && is_list_container<R>::value, void>::type
	cast(const L& left, R&right)
	{
		cast(left.elements, left.elementsCount, right);
	}
	// eRPC list type(ref) to thrift stub type(pointer)
	template<typename L, typename R>
	typename std::enable_if<is_list_struct<L>::value && is_list_container<R>::value, R>::type
	cast(const L& left, R*right)
	{
		R r;
		cast(left.elements, left.elementsCount, r);
		return std::move(r);
	}
	// eRPC list type(ref) to thrift stub type(pointer)
	template<typename L, typename R>
	typename std::enable_if<is_list_struct<L>::value && is_list_container<R>::value, R>::type
	cast(const L* left, R*right)
	{
		if (left) {
			return cast(*left, (R*)nullptr);
		}
		else {
			// return empty container
			return R();
		}
	}
	// eRPC type(array) to thrift stub type(pointer)
	template<typename E, typename R>
	typename std::enable_if<is_list_container<R>::value>::type
	cast(const E*left, size_t count, R* right)
	{
		typename std::remove_pointer<decltype(right)>::type::value_type r;
		if (left && count > 0) {
			cast(left, count, r);
		}
		return std::move(r);
	}
} /* namespace utils */	} /* namespace gdface */} /* namespace net */
#endif /* COMMON_SOURCE_CPP_ERPC_LIST_CAST_HPP_ */
